namespace OpenComposite.EII.RepositoryUIs
{
	using System;
	using System.Collections.Generic;
	using System.ComponentModel;
	using System.Windows.Forms;
	using OpenComposite.EII.Repository;
	using OpenComposite.EII.Services;

	[ToolboxItem(false)]
	public class RepositoryItemUI : UserControl
	{
		#region Initialization

		protected RepositoryItemUI()
		{
			this.ExcludedFromReadOnlyControls = new List<Control>();
		}

		protected RepositoryItemUI(IServiceProvider serviceProvider)
			: this()
		{
			this.serviceProvider = serviceProvider;

			this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
			this.SetStyle(ControlStyles.ResizeRedraw, false);
			this.UpdateStyles();
		}

		#endregion Initialization

		#region Public Members

		#region Events

		public event EventHandler AfterRepositoryItemChanged;

		public event EventHandler BeforeRepositoryItemChanged;

		public event EventHandler ReadOnlyChanged;

		#endregion Events

		#region Properties

		public virtual bool CanDelete
		{
			get
			{
				RepositoryItemBaseEx itemex = this.RepositoryItem as RepositoryItemBaseEx;
				if ( itemex != null ) {
					return ( itemex.IsLocked && itemex.LockedById == Global.CurrentUser.UserID );
				}
				return true;
			}
		}

		public virtual bool CanSave
		{
			get
			{
				RepositoryItemBaseEx itemex = this.RepositoryItem as RepositoryItemBaseEx;
				if ( itemex != null ) {
					return ( itemex.IsLocked && itemex.LockedById == Global.CurrentUser.UserID );
				}
				return true;
			}
		}

		[Browsable(false)]
		public virtual OpenComposite.EII.Repository.IRepositoryItem RepositoryItem
		{
			get { return item; }
			set
			{
				if ( value != item ) {
					OnBeforeRepositoryItemChanged();
					if ( value != null ) {
						value.ItemChanged += new OpenComposite.EII.Services.RepositoryChangedEventHandler(value_ItemChanged);
						value.ItemChanging += new RepositoryChangingEventHandler(value_ItemChanging);
						if ( item != null ) {
							item.ItemChanged -= value_ItemChanged;
							item.ItemChanging -= value_ItemChanging;
						}
					}
					item = value;
					OnAfterRepositoryItemChanged();
				}
			}
		}

		[Browsable(false)]
		public virtual string TypeDescription
		{
			get { return "Unknown Type."; }
		}

		[Browsable(false)]
		public virtual string TypeText
		{
			get { return "Unknown Type"; }
		}

		public bool ReadOnly
		{
			get { return _readonly; }
			set
			{
				if ( _readonly != value ) {
					_readonly = value;
					OnReadOnlyChanged();
				}
			}
		}

		#endregion Properties

		#endregion Public Members

		#region Protected Members

		#region Fields

		protected OpenComposite.EII.Repository.IRepositoryItem item;
		protected IServiceProvider serviceProvider;

		#endregion Fields

		#region Properties

		protected List<Control> ExcludedFromReadOnlyControls { get; private set; }

		#endregion

		#region Methods

		protected override object GetService(Type service)
		{
			if ( serviceProvider != null ) {
				return serviceProvider.GetService(service);
			} else {
				return base.GetService(service);
			}
		}

		protected object GetService<T>() where T : class
		{
			if ( serviceProvider != null ) {
				return serviceProvider.GetService(typeof(T)) as T;
			} else {
				return base.GetService(typeof(T)) as T;
			}
		}

		/// <summary>
		/// Returns a value indicating if the <see cref="RepositoryChangedEventArgs"/> contains information about the Name Property changed.
		/// </summary>
		/// <param name="e">The <see cref="OpenComposite.EII.Services.RepositoryChangedEventArgs"/> instance containing the event data.</param>
		/// <returns>
		/// 	<c>true</c> if name was changed; otherwise, <c>false</c>.
		/// </returns>
		protected bool IsNameChanged(RepositoryChangedEventArgs e)
		{
			return ( e.Member != null && e.Member.Name == "Name" && e.NewValue != null );
		}

		/// <summary>
		/// Returns a value indicating if the <see cref="RepositoryChangingEventArgs"/> contains information about the Name Property changed.
		/// </summary>
		/// <param name="e">The <see cref="OpenComposite.EII.Services.RepositoryChangedEventArgs"/> instance containing the event data.</param>
		/// <returns>
		/// 	<c>true</c> if name was changed; otherwise, <c>false</c>.
		/// </returns>
		protected bool IsNameChanged(RepositoryChangingEventArgs e)
		{
			return ( e.Member != null && e.Member.Name == "Name" && e.NewValue != null );
		}

		protected virtual void ItemChanged(RepositoryChangedEventArgs e)
		{
		}

		protected virtual void ItemChanging(RepositoryChangingEventArgs e)
		{
		}

		protected virtual void NotifyPropertyChange(string property, object value)
		{
		}

		protected virtual void OnAfterRepositoryItemChanged()
		{
			EventHandler tmp = this.AfterRepositoryItemChanged;
			if ( tmp != null ) tmp(this, EventArgs.Empty);
		}

		protected virtual void OnBeforeRepositoryItemChanged()
		{
			EventHandler tmp = this.BeforeRepositoryItemChanged;
			if ( tmp != null ) tmp(this, EventArgs.Empty);
		}

		protected virtual void OnReadOnlyChanged()
		{
			doReadOnlyChanged(this.ReadOnly, this);
			EventHandler tmp = this.ReadOnlyChanged;
			if ( tmp != null ) {
				tmp(this, EventArgs.Empty);
			}
		}
		#endregion Methods

		#endregion Protected Members

		#region Private Members

		#region Fields
		private bool _readonly = false;
		private List<Control> _disabledControls = null;
		#endregion

		#region Event Handlers

		private void value_ItemChanged(object sender, RepositoryChangedEventArgs e)
		{
			ItemChanged(e);
		}

		private void value_ItemChanging(object sender, RepositoryChangingEventArgs e)
		{
			if ( IsNameChanged(e) ) {
				if ( !Global.Data.RepositoryService.IsItemNameAvailable(e.RepositoryItem, e.NewValue.ToString()) ) {
					MessageBox.Show("An item with this name exists in the current domain." + Environment.NewLine + Environment.NewLine +
						"Please enter another name.",
						this.TypeText, MessageBoxButtons.OK, MessageBoxIcon.Stop);
					NotifyPropertyChange("Name", e.RepositoryItem.Name);
					e.Cancel = true;
				} else {
					NotifyPropertyChange("Name", e.NewValue);
				}
			}
			ItemChanging(e);
		}

		#endregion Event Handlers

		#region Methods

		private void InitializeComponent()
		{
			this.SuspendLayout();
			//
			// RepositoryItemUI
			//
			this.Name = "RepositoryItemUI";
			this.ResumeLayout(false);
		}

		private void doReadOnlyChanged(bool isreadonly, Control parentControl)
		{
			if ( _disabledControls == null ) _disabledControls = new List<Control>();
			foreach ( Control ctrl in parentControl.Controls ) {
				// ignore excluded controls
				if ( this.ExcludedFromReadOnlyControls.Contains(ctrl) ) continue; // NEXT

				TextBoxBase txt = ctrl as TextBoxBase;
				ScrollableControl pnl = ctrl as ScrollableControl;
				TabControl tab = ctrl as TabControl;
				GroupBox grp = ctrl as GroupBox;
				ButtonBase btn = ctrl as ButtonBase;
				DataGridView dgv = ctrl as DataGridView;
				ListControl lst = ctrl as ListControl;
				if ( txt != null ) {
					#region TextBoxBase
					if ( isreadonly ) {
						if ( txt.ReadOnly ) {
							_disabledControls.Add(txt);
						} else {
							if ( _disabledControls.Contains(txt) ) _disabledControls.Remove(txt);
							txt.ReadOnly = true;
						}
					} else {
						if ( !_disabledControls.Contains(txt) ) txt.ReadOnly = false;
					}
					#endregion
				} else if ( btn != null ) {
					#region ButtonBase
					if ( isreadonly ) {
						if ( !btn.Enabled ) {
							_disabledControls.Add(btn);
						} else {
							if ( _disabledControls.Contains(btn) ) _disabledControls.Remove(btn);
							btn.Enabled = false;
						}
					} else {
						if ( !_disabledControls.Contains(btn) ) btn.Enabled = true;
					}
					#endregion
				} else if ( lst != null ) {
					#region ListControl (ComboBox, ListBox, ...)
					if ( isreadonly ) {
						if ( !lst.Enabled ) {
							_disabledControls.Add(lst);
						} else {
							if ( _disabledControls.Contains(lst) ) _disabledControls.Remove(lst);
							lst.Enabled = false;
						}
					} else {
						if ( !_disabledControls.Contains(lst) ) lst.Enabled = true;
					}
					#endregion
				} else if ( dgv != null ) {
					#region DataGridView
					if ( isreadonly ) {
						if ( dgv.ReadOnly ) {
							_disabledControls.Add(dgv);
						} else {
							if ( _disabledControls.Contains(dgv) ) _disabledControls.Remove(dgv);
							dgv.ReadOnly = true;
						}
					} else {
						if ( !_disabledControls.Contains(dgv) ) dgv.ReadOnly = false;
					}
					#endregion
				} else if ( grp != null ) {
					#region GroupBox
					doReadOnlyChanged(isreadonly, grp);
					#endregion
				} else if ( tab != null ) {
					#region TabControl
					foreach ( TabPage page in tab.TabPages ) {
						doReadOnlyChanged(isreadonly, page);
					}
					#endregion
				} else if ( pnl != null ) {
					#region ScrollableControl
					doReadOnlyChanged(isreadonly, pnl);
					#endregion
				}
			}
		}

		#endregion Methods

		#endregion Private Members
	}
}